// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CImplGroup.
//   $Id$


#include "stdafx.h"
#include "ImplGroup.h"
#include "boundNode.h"
#include "model.h"
#include "GroupUI.h"
#include "DebugDisplayStrings.h"
#include "SinglenodeBinding.h"
#include "SinglenodeBindingUI.h"
#include "repeat.h"
void CImplGroup::getInstanceNode(IXMLDOMNode ** ppNode)
{
	//TODO: this should probably happen in buildDependecies
	if(false && m_bIsRepeatIteration)
	{
		//TODO- this object should be stored.
		CImplRepeat * pRepeat = dynamic_cast<CImplRepeat*>(m_pParentImpl);

		CVertex * vx = pRepeat->m_vertices.GetAt(m_ulParentOffset);
		CBoundNode * pBoundNode = vx->m_pInstanceNode;
		if(pBoundNode)
		{
			pBoundNode->getNode(ppNode);
		}

	}
	else
	{
		CImplSingleNodeBinding::getInstanceNode(ppNode);
#ifdef _DEBUG
		if(false)
		{
			CComBSTR s;
			(*ppNode)->get_xml(&s);
			::MessageBoxW(0,s,0,0);
		}
#endif
	}
}

void CImplGroup::buildDependencies()
{
	if(m_bIsRepeatIteration)
	{
		m_vertices.RemoveAll();
		ClearBindings();
		CImplRepeat * pRepeat = dynamic_cast<CImplRepeat*>(m_pParentImpl);
		if(pRepeat)
		{
			unsigned long iSize = static_cast<unsigned long>(pRepeat->m_vertices.GetSize());
			//If this iteration is within the list of nodes
			//	that the repeat represents.
			if(iSize > m_ulParentOffset + pRepeat->m_ulScrollOffset)
			{
				CVertex * vx = pRepeat->m_vertices.GetAt(pRepeat->m_ulScrollOffset + m_ulParentOffset);
	/*			vx->m_pInstanceNode->getNode(&pNode);
				if(pNode)
					m_pModel->addVertex(pNode,m_sRef,true,CVertex::EType::Instance,0,&m_vertices,GetIdResolver());
	*/
				//store the vertex in our own list
				m_vertices.SetAtGrow(0,vx);
				//make this a target of the node.
				vx->m_pInstanceNode->addTarget(this);
			}

			//Repeat will have sorted out the building of dependencies already.
			POSITION pos = m_ReliantControls.GetHeadPosition();
			while(pos)
			{
				m_ReliantControls.GetNext(pos)->buildDependencies();
			}

			if(m_pXForms->m_XFConfig.FormBrandingDebugInfo())
			{
				CComBSTR sDebugInfo;
				sDebugInfo.Attach(GenerateDebugDisplayInfo());
				m_pXFormsElement->put_XPathExpression(sDebugInfo);
			}

			ConfirmBindings();
		}
		else
		{
			ASSERT(0);
		}
	}
	else
		CImplSingleNodeBinding::buildDependencies();
}

CImplGroup::CImplGroup(CXFormsElementGroup* pElement)
	: CImplSingleNodeBinding((CXFormsElementSingleNodeBinding*) pElement)
	,m_ulParentOffset(0)
	,m_bIsRepeatIteration(false)
	,m_bIsRepeatIndex(false)
//	,m_pCaption(NULL)
	,m_pIdResolver(0)
{
	//Ref on group is optional, make it default to "." to prevent
	// binding error.
	//In the case that this is top-level, "." is obviously the default 
	//	context node, In the case that this is not top-level, but the 
	//	model attributes conflict, this is also the case.  If however, it is 
	//	within another binding element and the models do not conflict,
	//	the group will bind to the requested context node.
	m_sRef = L".";
//	m_pReliantControls = new CTypedPtrList<CPtrList, CImplNodeBinding*>;
	CFormControl::SetControlType(L"<group>");
}

CImplGroup::~CImplGroup()
{
//	if (m_pCaption)
//		delete m_pCaption;
	if(m_pIdResolver)
		delete m_pIdResolver;
	m_pIdResolver = 0;
	
	if(m_bIsRepeatIteration)
	{
		CImplRepeat * pRepeat = dynamic_cast<CImplRepeat *> (m_pParentImpl);
		if(pRepeat) 
			pRepeat->RemoveChild(this);
		if(m_pXForms)
		{
			POSITION pos = m_pXForms->m_IterationsToDelete.Find(this);
			if(pos)
				m_pXForms->m_IterationsToDelete.RemoveAt(pos);
		}
	}
}

HRESULT CImplGroup::showFocus()
{
	HRESULT hr = FireEvent(EVENT_FOCUS);
	if(SUCCEEDED(hr))
	{
		hr = S_FALSE;
		POSITION pos = m_Children.GetStartPosition();
		while(pos)
		{
			CImpl * pImpl = NULL;
			m_Children.GetNextAssoc(pos, pImpl, pImpl);
			if(pImpl)
			{
				CFormControl * pControl = dynamic_cast<CFormControl*>(pImpl);
				if(pControl)
				{
					hr = pControl->setFocus();
					break;
				}
			}
		}

	}
	return hr;
}

HRESULT CImplGroup::onDOMFocusIn_DefaultBehaviour()
{
	if(m_bIsRepeatIteration)
	{
		((CImplRepeat*)m_pParentImpl)->setCursor(m_ulParentOffset + ((CImplRepeat*)m_pParentImpl)->m_ulScrollOffset + 1);
	}
	return S_OK;
}
HRESULT CImplGroup::onactivate()
{
	if(m_bIsRepeatIteration)
	{
		((CImplRepeat*)m_pParentImpl)->setCursor(m_ulParentOffset + ((CImplRepeat*)m_pParentImpl)->m_ulScrollOffset + 1);
		m_pXForms->rebuild();
		m_pXForms->recalculate();
		m_pXForms->refresh();
	}
	else
		FireEvent(EVENT_ACTIVATE);

	return S_OK;
}


HRESULT CImplGroup::refresh()
{
	
	CComPtr<IXMLDOMNode> pNode;
	getInstanceNode(&pNode);

	// refresh the children with the new context node
	if(pNode)
	{
		POSITION pos = m_ReliantControls.GetHeadPosition();
		while(pos)
		{
			CImplNodeBinding * p = m_ReliantControls.GetNext(pos);
			if(FAILED(p->refresh()))
				return E_FAIL;
		}

	}
	else
	{
		//if there is no node, we can shortcut the 
		//	refresh by just blanking them out.
		//REMEMBER: this must be done as there may be out-of-date
		//	data visible in the control that needs to be removed.
		POSITION pos = m_ReliantControls.GetHeadPosition();
		while(pos)
		{
			if(FAILED(m_ReliantControls.GetNext(pos)->blank()))
				return E_FAIL;
		}
		ondisabled();
	}	
	FireStateEvents();

	return S_OK;
}


CIdResolver * CImplGroup::GetIdResolver() const
{
	//If this is a repeat iteration, 
	//	then the correct resolver is our own
	if(m_bIsRepeatIteration)
	{
		//This resolver is created by new.
		//	in init()
		return m_pIdResolver;
	}
	else
	{
		if(!m_pParentImpl && m_pXFormsElement)
			m_pXFormsElement->ResolveEnclosingBinding();
		//otherwise, use the conventional path to fetch an id resolver
		if(m_pParentImpl)
			return m_pParentImpl->GetIdResolver();
		else if(m_pXForms)
			return &(m_pXForms->m_IdResolver);
		else
			return 0;
	}
}

void CImplGroup::SetAsRepeatIteration(long lIndex)
{
	m_ulParentOffset  = lIndex;

	//This is the only place where this should happen - 
	//	Since SetAsRepeatIteration is part of the startup initialisation for this class
	//	albeit controlled from outside, m_bIsRepeatIteration can be considered conceptually
	//	const.
	const_cast<bool&>(m_bIsRepeatIteration) = true;

	//Since this is an iteration of repeat, it will need its
	//	own scoped id resolution.
	//To be conditionally deleted in the destructor.
	m_pIdResolver = new CIdResolver(m_pParentImpl,m_pXForms);
}


void CImplGroup::SetAsRepeatIndex(bool bIsIndex)
{
	if(m_bIsRepeatIteration)
	{
		m_bIsRepeatIndex = bIsIndex;
		if(m_pXFormsElement)
		{
			//if the UI exists, highlight the ui.
			//Don't set focus if within a currently inactive repeat iteration.
			bool bSetFocus = bIsIndex && !IsWithinRepeat(m_pParentImpl);
			m_pXFormsElement->highlight(m_bIsRepeatIndex,bSetFocus);
		}
	}
}

HRESULT CImplGroup::doInitialize()
{
	//this group may have been set as the repeat index 
	//	before initialize was called.
	HRESULT hr = S_FALSE;
	if(m_bIsRepeatIteration)
	{
		if(((CFormControl*)m_pParentImpl)->m_bInitializeDone)
		{
			if(m_bIsRepeatIndex)
			{
				bool bSetFocus =!IsWithinRepeat(m_pParentImpl);
				m_pXFormsElement->highlight(m_bIsRepeatIndex,bSetFocus);	
			}
			
		}
		hr =CImplSingleNodeBinding::doInitialize();
	}
	else
	{
		hr = CImplSingleNodeBinding::doInitialize();
	}
	return hr;
}

// blanking a group does nothing, as group has no value
HRESULT CImplGroup::blank(void)
{
	return S_OK; //Maybe S_FALSE?
}


BSTR CImplGroup::GenerateDebugDisplayInfo(void)
{
	CStringW sDebugInfo;
	if(m_bIsRepeatIteration)
	{
		CImplRepeat * pRepeat = reinterpret_cast<CImplRepeat*>(m_pParentImpl);
		CStringW sModel("");
		if(m_pModel)
		{
			sModel = m_pModel->m_sId;
		}
		const wchar_t * const * psReadOnly = &g_wsReadWrite;
		const wchar_t * const * psRelevant = &g_wsEnabled;
		const wchar_t * const * psValid = &g_wsValid;
		if(m_vertices.GetUpperBound()!=-1)
		{
			CBoundNode * pBN = m_vertices[0]->m_pInstanceNode;
			if(pBN)
			{
				if(pBN->isReadonly())
					psReadOnly = &g_wsReadOnly;
				if(!pBN->isRelevant())
					psRelevant = &g_wsDisabled;
				if(!pBN->isValid())
					psValid = &g_wsInvalid;
			}
		}
		const wchar_t * const * psRepeatIndex = m_bIsRepeatIndex?&g_wsTrue:&g_wsFalse;
		sDebugInfo.Format
		(
		L"model\t\"%s\"\nref\t\"(%s)[%i]\"\nrepeat-index\t%s\n%s, %s, %s"
			,sModel
			,pRepeat->m_sNodeset
			,pRepeat->m_ulScrollOffset + m_ulParentOffset
			,*psRepeatIndex
			,*psRelevant
			,*psReadOnly
			,*psValid
		);
	}
	return sDebugInfo.AllocSysString();
}